/**
 * ReportGear(2011)
 */
package com.reportgear.report.model.calculator;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.reportgear.report.model.ReportModel;
import com.reportgear.report.model.auxiliary.ColumnRow;
import com.reportgear.report.model.cell.Cell;
import com.reportgear.report.model.cell.value.FormulaObject;
import com.reportgear.report.model.script.Expression;
import com.reportgear.report.model.script.ScriptContext;
import com.reportgear.report.model.script.exception.DeathCycleException;
import com.sot.core.util.StringUtils;

/**
 * 
 * 公式计算
 * 
 * @version 1.0 2011-4-10
 * @author <a herf="lexloo@gmail.com">lexloo</a>
 * @since Report 1.0
 */
public class FormulaCalculator extends AbstractCalculator {
	// 单元格计算状态
	private boolean[][] cellStates;
	// 单元格计算队列，死循环判断
	private List<ColumnRow> executingColumnrowList = new ArrayList<ColumnRow>();
	// 单元格引用检测
	private static Pattern cellPattern = Pattern.compile("([A-Z]+[1-9][0-9]*)+?");
	// 表达式计算
	private Expression expr = ScriptContext.INSTANCE.getExpression();

	// TODO 当前不支持动态参数计算
	public FormulaCalculator() {

	}

	@Override
	public ReportModel calculate(ReportModel model, CalcParameter parameter) {
		if (logger.isDebugEnabled()) {
			logger.debug("计算模型：公式计算");
		}

		ReportModel calcModel = model;

		this.doCalculate(calcModel, parameter.getSessionInfo());

		return calcModel;
	}

	/**
	 * 计算
	 * 
	 * @param model
	 *            模型
	 * @param sessionInfo
	 *            session信息
	 */
	private void doCalculate(ReportModel model, SessionInfo sessionInfo) {
		ScriptContext.INSTANCE.setCalcReportModel(model);

		this.cellStates = new boolean[model.getColumnCount()][model.getRowCount()];

		for (Iterator<Cell> i = model.getCellIterator(); i.hasNext();) {
			this.calculateCell(model, i.next(), sessionInfo);
		}

		ScriptContext.INSTANCE.setCalcReportModel(null);
	}

	/**
	 * 计算单元格
	 * 
	 * @param model
	 *            模型
	 * @param cell
	 *            单元格
	 * @param sessionInfo
	 *            session信息
	 */
	private void calculateCell(ReportModel model, Cell cell, SessionInfo sessionInfo) {
		if (cell == null || cell.isMerged() || cell.getValue() == null) {
			return;
		}

		Object value = cell.getValue();

		if (!(value instanceof FormulaObject)) {
			return;
		}

		if (this.cellStates[cell.getColumn()][cell.getRow()]) {
			return;
		}

		ColumnRow cr = ColumnRow.valueOf(cell.getColumn(), cell.getRow());
		if (this.executingColumnrowList.contains(cr)) {
			throw new DeathCycleException("检测到死循环:" + this.executingColumnrowList);
		}

		this.executingColumnrowList.add(cr);

		// 公式中可能引用其他单元格，如果没有计算，先计算
		String content = StringUtils.convertToString(((FormulaObject) value).getFormula());
		content = this.updateSessionInfo(content, sessionInfo);
		Matcher m = cellPattern.matcher(content);
		while (m.find()) {
			ColumnRow crc = ColumnRow.valueOf(m.group(1));
			calculateCell(model, model.getCell(crc.getColumn(), crc.getRow()), sessionInfo);
		}

		cell.setValue(String.valueOf(expr.eval(content)));

		this.executingColumnrowList.remove(cr);
		this.cellStates[cr.getColumn()][cr.getRow()] = true;
	}

	/**
	 * 替换SESSION变量
	 * 
	 * @param content
	 * @param sessionInfo
	 * @return 更新
	 */
	private String updateSessionInfo(String content, SessionInfo sessionInfo) {
		String result = content.replaceAll("\\$\\$BRANCH", "\"" + sessionInfo.getBranchName() + "\"");
		result = result.replaceAll("\\$\\$ORGANIZATION", "\"" + sessionInfo.getOrganization() + "\"");
		result = result.replaceAll("\\$\\$POSITION", "\"" + sessionInfo.getPosition() + "\"");
		result = result.replaceAll("\\$\\$OPERATOR", "\"" + sessionInfo.getOperator() + "\"");

		return result;
	}
}
